#include "cam_wide.h"
#include "cam_wide_c.h"
#include "elog.h"
#include "elog_file.h"
#include "ws_constant.h"
#include "dwarf_utils.h"
#include "ws_response.h"
#include "cam_wide_mpi_enc.h"
#include "album_common.h"
#include "params_setting.h"
#include "timer_manager.h"
#include "timer_utils.h"
#include "album.h"
#include "state_manager.h"

#include "rkmedia_api.h"
#include "rkmedia_buffer.h"
#include "opencv2/opencv.hpp"

#include <libv4l2.h>
#include <sys/time.h>
#include <iostream>
#include <chrono>
#include <mutex>
#include <unordered_map>
#include <thread>
#include <functional>
#include <fstream>
#include <string>
#include <climits>

using namespace std;

static struct data_buffer {
    void* start;
    unsigned int length;
} * buffers;

static int buffers_length;

static int fd = -1;
static bool b_cam_open = false;
static bool b_cam_preview = true;
static int WIDTH = 1280, HEIGHT = 720;


//V4l2相关结构体
static struct v4l2_capability cap;
static struct v4l2_fmtdesc fmtdesc;
static struct v4l2_frmsizeenum frmsizeenum;
static struct v4l2_format v4l2_format;
static struct v4l2_requestbuffers reqbuf;
static struct v4l2_buffer buffer;

static const string photo_name_prefix = "DWARF_WIDE_";                   //照片文件名前缀
static const string burst_photo_path_prefix = "DWARF_WIDE_BURST_";       //连拍文件夹前缀
static const string timelapse_name_prefix = "DWARF_WIDE_TL_";            //延时摄影文件名前缀 
 
static mutex camera_mutex;

//jpg图传相关
static uint8_t* jpg_ptr = NULL;
static int jpg_size;
static pthread_mutex_t jpg_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  jpg_cond = PTHREAD_COND_INITIALIZER;

//拍照相关
static CamWidePhotoState photo_state = CAM_WIDE_PHOTO_IDLE;
static int burst_photo_count = 3;                   //连拍默认数量
static int burst_photo_id = 0;                      //连拍图片id
static string burst_photo_path;
static int burst_photo_interval = 0;

//延时摄影相关
static CamWideTimelapseState timelapse_state = CAM_WIDE_TIMELAPSE_IDLE;
static uint8_t* timelapse_nv12_ptr = NULL;
static int timelapse_nv12_size;
static pthread_mutex_t timelapse_nv12_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  timelapse_nv12_cond = PTHREAD_COND_INITIALIZER;
static std::atomic<int> record_loading_count = 0;

static StateManager* state_manager = StateManager::GetInstance();
// 相册管理
static Album m_album = Album();
static TimerManager timelapse_timer_manager = TimerManager();
static int timelapse_interval = 1;
static int timelapse_out_time = 1;    // 已成片时间
static int timelapse_elapsed_time = 1;

// 曝光
static bool b_auto_exposure = true;
static std::vector<ExposureSetting> exp_settings = {
    {0, 0.0001, "1/10000"},
    {3, 0.0004, "1/2500"},
    {6, 0.0005, "1/2000"},
    {9, 0.000625, "1/1600"},
    {12, 0.0008, "1/1250"},
    {15, 0.001, "1/1000"},
    {18, 0.00125, "1/800"},
    {21, 0.0015625, "1/640"},
    {24, 0.002, "1/500"},
    {27, 0.0025, "1/400"},
    {30, 0.003125, "1/320"},
    {33, 0.004, "1/250"},
    {36, 0.005, "1/200"},
    {39, 0.00625, "1/160"},
    {42, 0.008, "1/125"},
    {45, 0.01, "1/100"},
    {48, 0.0125, "1/80"},
    {51, 0.0166667, "1/60"},
    {54, 0.02, "1/50"},
    {57, 0.025, "1/40"},
    {60, 0.0333333, "1/30"},
    {63, 0.04, "1/25"},
    {66, 0.05, "1/20"},
    {69, 0.0666667, "1/15"},
    {72, 0.0769231, "1/13"},
    {75, 0.1, "1/10"},
    {78, 0.125, "1/8"},
    {81, 0.1666667, "1/6"},
    {84, 0.2, "1/5"},
    {87, 0.25, "1/4"},
    {90, 0.3333333, "1/3"},
    {93, 0.4, "0.4"},
    {96, 0.5, "0.5"},
    {99, 0.6, "0.6"},
    {102, 0.8, "0.8"},
    {105, 1.0, "1.0"}
};


// 增益
static std::vector<IntValueSetting> gain_settings = {
    {0, 60},
    {3, 70},
    {6, 80},
    {9, 90},
    {12, 100},
    {15, 110},
    {18, 120},
    {21, 130},
    {24, 140},
    {27, 150},
    {30, 160}
};

// 白平衡
static bool b_auto_wb = true;
static std::vector<IntValueSetting> wb_settings = {
    {0, 2800},
    {3, 2900},
    {6, 3000},
    {9, 3100},
    {12, 3200},
    {15, 3300},
    {18, 3400},
    {21, 3500},
    {24, 3600},
    {27, 3700},
    {30, 3800},
    {33, 3900},
    {36, 4000},
    {39, 4100},
    {42, 4200},
    {45, 4300},
    {48, 4400},
    {51, 4500},
    {54, 4600},
    {57, 4700},
    {60, 4800},
    {63, 4900},
    {66, 5000},
    {69, 5100},
    {72, 5200},
    {75, 5300},
    {78, 5400},
    {81, 5500},
    {84, 5600},
    {87, 5700},
    {90, 5800},
    {93, 5900},
    {96, 6000}
};

void getCamWideJgpFrame(uint8_t* &frame, int &frame_size) {
    if (b_cam_open) {
        pthread_mutex_lock(&jpg_mutex);
        pthread_cond_wait(&jpg_cond, &jpg_mutex);
        frame = (uint8_t*)malloc(jpg_size);
        frame_size = jpg_size;
        // log_i("cam wide get jpg size = %d", frame_size);
        memcpy(frame, jpg_ptr, jpg_size);
        pthread_mutex_unlock(&jpg_mutex);
    }
}

// void getCamWideJgpFrame(uint8_t* &frame, int &frame_size) {
//     pthread_mutex_lock(&jpg_mutex);
//     pthread_cond_wait(&jpg_cond, &jpg_mutex);

//     // Reallocate memory using realloc
//     uint8_t* new_frame = (uint8_t*)realloc(frame, jpg_size);
//     if (new_frame == NULL) {
//         // Handle memory allocation error
//         pthread_mutex_unlock(&jpg_mutex);
//         return;
//     }
    
//     frame = new_frame;
//     frame_size = jpg_size;
    
//     // Copy data to the new frame
//     memcpy(frame, jpg_ptr, jpg_size);
    
//     pthread_mutex_unlock(&jpg_mutex);
// }


void getCamWideNv12Frame(uint8_t** frame, int* frame_size) {
    pthread_mutex_lock(&timelapse_nv12_mutex);
    pthread_cond_wait(&timelapse_nv12_cond, &timelapse_nv12_mutex);
    *frame = (uint8_t*)malloc(timelapse_nv12_size);
    *frame_size = timelapse_nv12_size;
    memcpy(*frame, timelapse_nv12_ptr, timelapse_nv12_size);
    // log_i("get nv12 frame size = %d, start = %d, end = %d", *frame_size, **frame, *(*frame + *frame_size - 1));
    pthread_mutex_unlock(&timelapse_nv12_mutex);
}

int CamWide::saveJpg(const string& jpg_path, uint8_t* frame, int frame_size) {
    std::ofstream file(jpg_path, std::ios::binary);

    if (file.is_open()) {
        file.write((const char*)frame, frame_size);

        if (!file)  // 写入错误
            return -WS_SDCARD_WRITE_ERROR;

        file.close();
        system("sync");

        return 0;
    } else {
        return -WS_SDCARD_WRITE_ERROR;
    }
}

int getCamWideTimelapState() {
    return timelapse_state;
}

cv::Mat decodeJpegToNV12(uint8_t* jpeg_data, size_t jpeg_size) {
    // 将JPEG数据加载到内存中的cv::Mat对象中
    cv::Mat jpeg_mat(1, jpeg_size, CV_8UC1, jpeg_data);
    
    // 使用OpenCV解码JPEG图像
    cv::Mat bgr_mat = cv::imdecode(jpeg_mat, cv::IMREAD_UNCHANGED);

    //BGR转NV12
    int height = bgr_mat.rows;
    int width = bgr_mat.cols;

    cv::Mat img_nv12;
    cv::Mat yuv_mat;
    cv::cvtColor(bgr_mat, yuv_mat, cv::COLOR_BGR2YUV_I420);

    uint8_t *yuv = yuv_mat.ptr<uint8_t>();
    img_nv12 = cv::Mat(height * 3 / 2, width, CV_8UC1);
    uint8_t *ynv12 = img_nv12.ptr<uint8_t>();

    int32_t uv_height = height / 2;
    int32_t uv_width = width / 2;

    // copy y data
    int32_t y_size = height * width;
    memcpy(ynv12, yuv, y_size);

    // copy uv data
    uint8_t *nv12 = ynv12 + y_size;
    uint8_t *u_data = yuv + y_size;
    uint8_t *v_data = u_data + uv_height * uv_width;

    for (int32_t i = 0; i < uv_width * uv_height; i++) {
        *nv12++ = *u_data++;
        *nv12++ = *v_data++;
    }
    
    return img_nv12;
}

CamWide::CamWide() {

}

CamWide::~CamWide() {

}

static unordered_map<int, int (CamWide::*)(WsPacket& ws_packet)> camera_func_table {
    { CMD_CAMERA_WIDE_OPEN_CAMERA, &CamWide::openCamera },
    { CMD_CAMERA_WIDE_CLOSE_CAMERA, &CamWide::closeCamera },
    { CMD_CAMERA_WIDE_SET_EXP_MODE, &CamWide::setExpMode },
    { CMD_CAMERA_WIDE_GET_EXP_MODE, &CamWide::getExpMode },
    { CMD_CAMERA_WIDE_SET_EXP, &CamWide::setExp },
    { CMD_CAMERA_WIDE_GET_EXP, &CamWide::getExp },
    { CMD_CAMERA_WIDE_SET_GAIN, &CamWide::setGain },
    { CMD_CAMERA_WIDE_GET_GAIN, &CamWide::getGain },
    { CMD_CAMERA_WIDE_SET_BRIGHTNESS, &CamWide::setBrightness },
    { CMD_CAMERA_WIDE_GET_BRIGHTNESS, &CamWide::getBrightness },
    { CMD_CAMERA_WIDE_SET_CONTRAST, &CamWide::setContrast },
    { CMD_CAMERA_WIDE_GET_CONTRAST, &CamWide::getContrast },
    { CMD_CAMERA_WIDE_SET_SATURATION, &CamWide::setSaturation },
    { CMD_CAMERA_WIDE_GET_SATURATION, &CamWide::getSaturation },
    { CMD_CAMERA_WIDE_SET_HUE, &CamWide::setHue },
    { CMD_CAMERA_WIDE_GET_HUE, &CamWide::getHue },
    { CMD_CAMERA_WIDE_SET_SHARPNESS, &CamWide::setSharpness },
    { CMD_CAMERA_WIDE_GET_SHARPNESS, &CamWide::getSharpness },
    { CMD_CAMERA_WIDE_SET_WB_MODE, &CamWide::setWBMode },
    { CMD_CAMERA_WIDE_GET_WB_MODE, &CamWide::setWBMode },
    { CMD_CAMERA_WIDE_SET_WB_CT, &CamWide::setWBCT },
    { CMD_CAMERA_WIDE_GET_WB_CT, &CamWide::getWBCT }, 
    { CMD_CAMERA_WIDE_PHOTOGRAPH, &CamWide::photograph },
    { CMD_CAMERA_WIDE_BURST, &CamWide::burstPhoto },
    { CMD_CAMERA_WIDE_STOP_BURST, &CamWide::stopBurstPhoto },
    { CMD_CAMERA_WIDE_START_TIMELAPSE_PHOTO, &CamWide::startTimeLapsePhotograph },
    { CMD_CAMERA_WIDE_STOP_TIMELAPSE_PHOTO, &CamWide::stopTimeLapsePhotograph },
    { CMD_CAMERA_WIDE_GET_ALL_PARAMS, &CamWide::getAllParams }
};

int CamWide::handleMessage(WsPacket& ws_packet) {
    log_i("handle cam wide message, cmd = %d", ws_packet.cmd());

    //在表中查找函数指针并调用
    auto it = camera_func_table.find(ws_packet.cmd());
    if (it != camera_func_table.end()) {
        (this->*(it->second))(ws_packet);
    }
    else {
        log_i("Function not found");
    }

    return 0;
}

int CamWide::isCameraOpen() {
    if (!b_cam_open) {
        log_e("cam wide is closed");
        return -CODE_CAMERA_WIDE_CLOSED;
    }

    return 0;
}

int CamWide::isCameraClose() {
    if (b_cam_open) {
        log_e("cam wide is opened");
        return -CODE_CAMERA_WIDE_OPENED;
    }

    return 0;
}

int CamWide::openCamera(bool binning) {
    unique_lock<mutex> lock(camera_mutex);
    if ((isCameraClose()) < 0) {
        wsCommonResponse(CMD_CAMERA_WIDE_OPEN_CAMERA, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, WS_OK);
        return 0;
    }
    log_i("cam wide open camera");

    b_cam_preview = true;

    //检测USB摄像头
    FILE *fp;
    char line[100];
    char device[100];
    bool find_uvc = false;

    fp = popen("v4l2-ctl --list-devices", "r");
	if (fp != NULL) {
		while (fgets(line, 100, fp) != NULL) {
			if(strstr(line, "usb-ffe00000")) {
                find_uvc = true;
				fgets(line, 100, fp);
                log_i("line is %s", line);
                sscanf(line, "        %s", device);
                log_i("device is %s", device);
			}
        }
    
        pclose(fp);
    }

    if(!find_uvc) {
        log_e("cannot find UVC camera");
        wsCommonResponse(CMD_CAMERA_WIDE_OPEN_CAMERA, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, -CODE_CAMERA_WIDE_CANNOT_FOUND);
        return -CODE_CAMERA_WIDE_CANNOT_FOUND;
    }

    fd = open(device, O_RDWR);
    if (fd == -1) {
        log_e("open uvc camera failed");
        wsCommonResponse(CMD_CAMERA_WIDE_OPEN_CAMERA, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, -CODE_CAMERA_WIDE_OPEN_FAILED);
        return -CODE_CAMERA_WIDE_OPEN_FAILED;
    }

    int ret = 0;

    memset(&v4l2_format, 0, sizeof(v4l2_format));
    v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    v4l2_format.fmt.pix.width = WIDTH;
    v4l2_format.fmt.pix.height = HEIGHT;
    v4l2_format.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
    if (-1 == ioctl(fd, VIDIOC_S_FMT, &v4l2_format)) {
        log_e("open uvc camera failed, VIDIOC_S_FMT error:%s", strerror(errno));
        ret = -CODE_CAMERA_WIDE_OPEN_FAILED;
        goto __exit;
    }

    //申请帧缓存区
    memset(&reqbuf, 0, sizeof(reqbuf));
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    reqbuf.count = 3;

    if (-1 == ioctl(fd, VIDIOC_REQBUFS, &reqbuf)) {
        log_e("open uvc camera failed, VIDIOC_REQBUFS error:%s", strerror(errno));
        ret = -CODE_CAMERA_WIDE_OPEN_FAILED;
        goto __exit;
    }

    //分配缓存区
    buffers = (data_buffer*)calloc(reqbuf.count, sizeof(*buffers));
    if (buffers == NULL) {
        log_e("open uvc camera failed, calloc buffer error");
        ret = -CODE_CAMERA_WIDE_OPEN_FAILED;
        goto __exit;
    }

    // mmap内存映射
    for (int i = 0; i < (int)reqbuf.count; i++) {
        memset(&buffer, 0, sizeof(buffer));
        buffer.type = reqbuf.type;
        buffer.memory = V4L2_MEMORY_MMAP;
        buffer.index = i;

        if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buffer)) {
            log_e("open uvc camera failed, VIDIOC_QUERYBUF error:%s", strerror(errno));
            ret = -CODE_CAMERA_WIDE_OPEN_FAILED;
            goto __exit;
        }

        buffers[i].length = buffer.length;
        buffers[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer.m.offset);
        if (MAP_FAILED == buffers[i].start) {
            log_e("open uvc camera failed, mmap error");
            ret = -CODE_CAMERA_WIDE_OPEN_FAILED;
            goto __exit;
        }

        if (ioctl(fd, VIDIOC_QBUF, &buffer) == -1) {
            log_e("open uvc camera failed, VIDIOC_QBUF error:%s", strerror(errno));
            ret = -CODE_CAMERA_WIDE_OPEN_FAILED;
            goto __exit;
        }
    }

    enum v4l2_buf_type type;
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
        log_e("open uvc camera failed, VIDIOC_STREAMON error:%s", strerror(errno));
        ret = -CODE_CAMERA_WIDE_OPEN_FAILED;
        goto __exit;
    }

    b_cam_open = true;
    b_auto_exposure = true;
    lock.unlock();

    wsCommonResponse(CMD_CAMERA_WIDE_OPEN_CAMERA, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, WS_OK);

    while(b_cam_preview) {
        memset(&buffer, 0, sizeof(buffer));
        buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buffer.memory = V4L2_MEMORY_MMAP;

        if (ioctl(fd, VIDIOC_DQBUF, &buffer) == -1) {
            log_e("open uvc camera failed, VIDIOC_DQBUF error:%s", strerror(errno));
            break;
        }

        pthread_mutex_lock(&jpg_mutex);
        jpg_ptr = (uint8_t*)buffers[buffer.index].start;
        jpg_size = buffer.bytesused;
        pthread_cond_broadcast(&jpg_cond);
        pthread_mutex_unlock(&jpg_mutex);

        if (ioctl(fd, VIDIOC_QBUF, &buffer) < 0) {
            log_e("open uvc camera failed, VIDIOC_QBUF error:%s", strerror(errno));
            break;
        }
    }

    //关闭视频流
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
        log_e("VIDIOC_STREAMOFF failed:%s", strerror(errno));
        goto __exit;
    }

__exit:
    // 手动释放分配的内存
    if(buffers != NULL) {
        for (int i = 0; i < (int)reqbuf.count; i++) {
            if (-1 == munmap(buffers[i].start, buffers[i].length)) {
                log_e("munmap:%s", strerror(errno));
            }
        }

        for (int i = 0; i < 5; i++) {
            struct v4l2_requestbuffers reqbuf_tmp = {0};
            reqbuf_tmp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            reqbuf_tmp.memory = V4L2_MEMORY_MMAP;
            reqbuf_tmp.count = 0;
            if (-1 == ioctl(fd, VIDIOC_REQBUFS, &reqbuf_tmp)) {
                log_e("VIDIOC_REQBUFS:%s", strerror(errno));
                usleep(50 * 1000);
            } else {
                break;
            }
        }

        xfree(buffers);

        log_i("uvc camera free done");
    }

    close(fd);
    fd = -1;

    b_cam_open = false;

    if (b_cam_preview) {
        wsCommonResponse(CMD_CAMERA_WIDE_OPEN_CAMERA, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);
    } else {
        wsCommonResponse(CMD_CAMERA_WIDE_CLOSE_CAMERA, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);
    } 

    return ret;
}

int CamWide::openCamera(WsPacket& ws_packet) {
    int ret;
    ReqOpenCamera req_open_camera;
    string serialized_data = ws_packet.data();

    if (!req_open_camera.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    log_i("binning = %d", req_open_camera.binning());  
    ret = openCamera(req_open_camera.binning());

    return ret;
}

int CamWide::closeCamera() {
    lock_guard<mutex> lock(camera_mutex);
    if ((isCameraOpen()) < 0) {
        return 0;
    }
    log_i("cam wide close camera");

    b_cam_preview = false;

    if (fd < 0)
        return 0;

    // 关闭视频流
    enum v4l2_buf_type type;
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMOFF, &type) == -1) {
        log_e("VIDIOC_STREAMOFF failed:%s", strerror(errno));
    }

    return 0;
}

int CamWide::closeCamera(WsPacket& ws_packet) {
    wsCommonResponse(CMD_CAMERA_WIDE_CLOSE_CAMERA, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, WS_OK);

    return 0;
}

int CamWide::setExpMode(int mode) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_EXPOSURE_AUTO;
    if (mode == 0) {
        control.value = 3;
        b_auto_exposure = true;
    } else {
        control.value = mode;
        b_auto_exposure = false;
    }
    
    if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1){
        log_e("set exp mode failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    log_i("set exp mode to %d", mode);

    wsResNotifyParam(CMD_NOTIFY_WIDE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, mode, 0, 0, 75, 0);

    return ret;
}

int CamWide::setExpMode(WsPacket& ws_packet) {
    int ret;
    ReqSetExpMode req_set_exp_mode;

    string serialized_data = ws_packet.data();
    if (!req_set_exp_mode.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    ret = setExpMode(req_set_exp_mode.mode());
    wsCommonResponse(CMD_CAMERA_WIDE_SET_EXP_MODE, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    return ret;
}

int CamWide::getExpMode(int *mode) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_EXPOSURE_AUTO;
    if(ioctl(fd,VIDIOC_G_CTRL,&control) == -1){
        log_i("get exp mode failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    *mode = control.value;
    log_i("get exp mode = %d", *mode);

    return ret;
}

int CamWide::getExpMode(WsPacket& ws_packet) {
    int mode;
    int ret = getExpMode(&mode);
    wsCommonResWithInt(CMD_CAMERA_WIDE_GET_EXP_MODE, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, mode, ret);

    return ret;
}

int CamWide::setExp(double value) {
    int ret;
    v4l2_control control;

    log_i("setExp expMode = %d", b_auto_exposure);
   // 如果摄像头打开了，并且是自动曝光，先设置为手动曝光
    if (b_cam_open && b_auto_exposure) {
        setExpMode(1);
        usleep(50 * 1000);
    }

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_EXPOSURE_ABSOLUTE;
    control.value = value * 10000;
    if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1){
        log_i("set exp failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    log_i("set exp to %d", control.value);

    int index = findClosestExpIndex(exp_settings, value);
    log_i("set exp index = %d", index);
    wsResNotifyParam(CMD_NOTIFY_WIDE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, 1, 0, 0, index, 0);

    return 0;
}

int CamWide::setExp(WsPacket& ws_packet) {
    int ret;
    ReqSetExp req_set_exp;

    string serialized_data = ws_packet.data();
    if (!req_set_exp.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    double exp_value = getExpByIndex(exp_settings, req_set_exp.index());

    if (exp_value > 0) {
        ret = setExp(exp_value);
    } else {
        ret = -WS_INVAID_PARAM;
    }

    wsCommonResponse(CMD_CAMERA_WIDE_SET_EXP, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    return ret;
}

int CamWide::getExp(double *value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_EXPOSURE_ABSOLUTE;
    if(ioctl(fd,VIDIOC_G_CTRL,&control) == -1){
        log_i("get exp failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    *value = control.value * 1.0 / 10000;
    log_i("get exp = %f", *value);
    return ret;
}

int CamWide::getExp(WsPacket& ws_packet) {
    double value;
    int ret = getExp(&value);
    wsCommonResWithDouble(CMD_CAMERA_WIDE_GET_EXP, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamWide::setGain(int value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_GAIN;
    control.value = value;
    if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1){
        log_i("set gain failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    log_i("set gain to %d", value);

    int index = findClosestIntValueIndex(gain_settings, value);
    wsResNotifyParam(CMD_NOTIFY_WIDE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, 1, 1, 0, index, 0);

    return 0;
}

int CamWide::setGain(WsPacket& ws_packet) {
    int ret;
    ReqSetGain req_set_gain;

    string serialized_data = ws_packet.data();
    if (!req_set_gain.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = getIntValueByIndex(gain_settings, req_set_gain.index());
    if (value > 0) {
        ret = setGain(value);
    } else {
        ret = -WS_INVAID_PARAM;
    }
   
    wsCommonResponse(CMD_CAMERA_WIDE_SET_GAIN, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    return ret;
}

int CamWide::getGain(int *value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_GAIN;
    if(ioctl(fd,VIDIOC_G_CTRL,&control) == -1){
        log_i("get gain failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    *value = control.value;
    log_i("get gain = %d", *value);
    return ret;
}

int CamWide::getGain(WsPacket& ws_packet) {
    int gain;
    int ret = getGain(&gain);
    wsCommonResWithInt(CMD_CAMERA_WIDE_GET_GAIN, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, gain, ret);

    return ret;
}

int CamWide::setBrightness(int value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_BRIGHTNESS;
    control.value = value;
    if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1){
        log_i("set brightness failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    log_i("set brightness to %d", value);

    return 0;
}

int CamWide::setBrightness(WsPacket& ws_packet) {
    int ret;
    ReqSetBrightness req_set_brightness;

    string serialized_data = ws_packet.data();
    if (!req_set_brightness.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = req_set_brightness.value() * 64.0 / 100;
    ret = setBrightness(value);
    wsCommonResponse(CMD_CAMERA_WIDE_SET_BRIGHTNESS, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_WIDE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 0, 3, 1, 0, req_set_brightness.value());
    }

    return ret;
}

int CamWide::getBrightness(int *value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_BRIGHTNESS;
    if(ioctl(fd,VIDIOC_G_CTRL,&control) == -1){
        log_i("get brightness failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    *value = control.value;
    log_i("get brightness %d", control.value);

    return ret;
}

int CamWide::getBrightness(WsPacket& ws_packet) {
    int value;
    int ret = getBrightness(&value);
    wsCommonResWithInt(CMD_CAMERA_WIDE_GET_GAIN, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamWide::setContrast(int value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_CONTRAST;
    control.value = value;
    if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1){
        log_i("set contrast failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    log_i("set contrast to %d", value);

    return 0;    
}

int CamWide::setContrast(WsPacket& ws_packet) {
    int ret;
    ReqSetContrast req_set_contrast;

    string serialized_data = ws_packet.data();
    if (!req_set_contrast.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = (req_set_contrast.value() + 100) * 95.0 / 200;
    ret = setContrast(value);
    wsCommonResponse(CMD_CAMERA_WIDE_SET_CONTRAST, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_WIDE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 0, 4, 1, 0, req_set_contrast.value());
    }

    return ret;
}

int CamWide::getContrast(int *value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_CONTRAST;
    if(ioctl(fd,VIDIOC_G_CTRL,&control) == -1){
        log_i("get contrast failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    *value = control.value;
    log_i("get contrast = %d", *value);

    return control.value;
}

int CamWide::getContrast(WsPacket& ws_packet) {
    int value;
    int ret = getContrast(&value);
    wsCommonResWithInt(CMD_CAMERA_WIDE_GET_CONTRAST, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamWide::setSaturation(int value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_SATURATION;
    control.value = value;
    if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1){
        log_i("set saturation failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    log_i("set saturation to %d", value);

    return 0;    
}

int CamWide::setSaturation(WsPacket& ws_packet) {
    int ret;
    ReqSetSaturation req_set_saturation;

    string serialized_data = ws_packet.data();
    if (!req_set_saturation.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = (req_set_saturation.value() + 100) * 100.0 / 200;
    ret = setSaturation(value);
    wsCommonResponse(CMD_CAMERA_WIDE_SET_SATURATION, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_WIDE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 0, 6, 1, 0, req_set_saturation.value());
    }

    return ret;
}

int CamWide::getSaturation(int *value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_SATURATION;
    if(ioctl(fd,VIDIOC_G_CTRL,&control) == -1){
        log_i("get saturation failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    *value = control.value;
    log_i("get saturation = %d", *value);

    return ret;
}
 
int CamWide::getSaturation(WsPacket& ws_packet) {
    int value;
    int ret = getContrast(&value);
    wsCommonResWithInt(CMD_CAMERA_WIDE_GET_SATURATION, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamWide::setHue(int value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_HUE;
    control.value = value;
    if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1){
        log_i("set hue failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    log_i("set hue to %d", control.value);

    return 0;
}

int CamWide::setHue(WsPacket& ws_packet) {
    int ret;
    ReqSetHue req_set_hue;

    string serialized_data = ws_packet.data();
    if (!req_set_hue.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = req_set_hue.value() * 2000.0 / 180;
    ret = setHue(value);
    wsCommonResponse(CMD_CAMERA_WIDE_SET_HUE, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_WIDE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 0, 5, 1, 0, req_set_hue.value());
    }

    return 0;
}

int CamWide::getHue(int *value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_HUE;
    if(ioctl(fd,VIDIOC_G_CTRL,&control) == -1){
        log_i("get hue failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    *value = control.value;
    log_i("get hue = %d", *value);

    return ret;
}

int CamWide::getHue(WsPacket& ws_packet) {
    int value;
    int ret = getHue(&value);
    wsCommonResWithInt(CMD_CAMERA_WIDE_GET_HUE, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamWide::setSharpness(int value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_SHARPNESS;
    control.value = value;
    if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1){
        log_i("set sharpness failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    log_i("set sharpness to %d", control.value);

    return 0;
}

int CamWide::setSharpness(WsPacket& ws_packet) {
    int ret;
    ReqSetSharpness req_set_sharpness;

    string serialized_data = ws_packet.data();
    if (!req_set_sharpness.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = req_set_sharpness.value() * 6.0 / 100 + 1; 
    ret = setSharpness(value);
    wsCommonResponse(CMD_CAMERA_WIDE_SET_SHARPNESS, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_WIDE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 0, 7, 1, 0, req_set_sharpness.value());
    }

    return 0;
}

int CamWide::getSharpness(int *value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_SHARPNESS;
    if(ioctl(fd,VIDIOC_G_CTRL,&control) == -1){
        log_i("get sharpness failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    *value = control.value;
    log_i("get sharpness = %d", *value);

    return ret;
}

int CamWide::getSharpness(WsPacket& ws_packet) {
    int value;
    int ret = getSharpness(&value);
    wsCommonResWithInt(CMD_CAMERA_WIDE_GET_SHARPNESS, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamWide::setWBMode(int mode) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_AUTO_WHITE_BALANCE;
    if (0 == mode)
        control.value = 1;
    if (1 == mode)
        control.value = 0;
        
    if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1){
        log_i("set WBT failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    log_i("set wb mode to %d", mode);
    b_auto_wb = mode == 0 ? true : false;

    return 0;
}

int CamWide::setWBMode(WsPacket& ws_packet) {
    int ret;
    ReqSetWBMode req_set_wb_mode;

    string serialized_data = ws_packet.data();
    if (!req_set_wb_mode.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }
    
    ret = setWBMode(req_set_wb_mode.mode());
    wsCommonResponse(CMD_CAMERA_WIDE_SET_WB_MODE, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_WIDE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, req_set_wb_mode.mode(), 2, 0, 0, 0);
    }

    return 0;
}

int CamWide::getWBMode(int *value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_AUTO_WHITE_BALANCE;  
    if(ioctl(fd,VIDIOC_G_CTRL,&control) == -1){
        log_i("get wb mode failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    *value = control.value;
    log_i("get wb mode = %d", *value);

    return ret;
}

int CamWide::getWBMode(WsPacket& ws_packet) {
    int value;
    int ret = getWBMode(&value);
    wsCommonResWithInt(CMD_CAMERA_WIDE_GET_WB_MODE, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamWide::setWBCT(int value) {
   int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    if (b_auto_wb) {
        control.id = V4L2_CID_AUTO_WHITE_BALANCE;
        control.value = 0;
            
        if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1){
            log_i("set WBT failed");
            return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
        }
        b_auto_wb = false;
    }

    control.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
    control.value = value;
    if(ioctl(fd,VIDIOC_S_CTRL,&control) == -1) {
        log_i("set wbct failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    log_i("set wbct to %d", value);

    return 0;
}

int CamWide::setWBCT(WsPacket& ws_packet) {
    int ret;
    ReqSetWBCT req_set_wbct;

    string serialized_data = ws_packet.data();
    if (!req_set_wbct.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = getIntValueByIndex(wb_settings, req_set_wbct.index());
    if (value > 0) {
        ret = setWBCT(value);
    } else {
        ret = -WS_INVAID_PARAM;
    }

    wsCommonResponse(CMD_CAMERA_WIDE_SET_WB_CT, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_WIDE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, 1, 2, 0, req_set_wbct.index(), 0);
    }

    return 0;
}

int CamWide::getWBCT(int *value) {
    int ret;
    v4l2_control control;
   
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    control.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE;
    if(ioctl(fd,VIDIOC_G_CTRL,&control) == -1) {
        log_i("get wb ct failed");
        return -CODE_CAMERA_WIDE_SET_ISP_FAILED;
    }
    *value = control.value;
    log_i("get wb ct = %d", *value);

    return ret;
}

int CamWide::getWBCT(WsPacket& ws_packet) {
    int value;
    int ret = getWBCT(&value);
    wsCommonResWithInt(CMD_CAMERA_WIDE_GET_WB_CT, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamWide::photograph() {
    int ret;
    uint8_t* frame = NULL;
    int frame_size = 0;
    string photo_path;
    
    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        wsCommonResponse(CMD_CAMERA_WIDE_PHOTOGRAPH, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);
        return ret;
    }

    if (getPhotoState() != CAM_WIDE_PHOTO_IDLE) {
        log_e("cam tele is photographing");
        wsCommonResponse(CMD_CAMERA_WIDE_PHOTOGRAPH, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, WS_OK);
        return 0;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");
        wsCommonResponse(CMD_CAMERA_WIDE_PHOTOGRAPH, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, -WS_SDCARD_NOT_EXIST);
        return -WS_SDCARD_NOT_EXIST;
    }

    wsResNotifyCamFunctionState(CMD_NOTIFY_WIDE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_PHOTO, STATE_RUNNING);

    string file_name = getTimeStamp();
    photo_path = album_path + photograph_path + photo_name_prefix + file_name + ".jpg";
    log_i("photograph, file name is %s", photo_path.c_str());
    getCamWideJgpFrame(frame, frame_size);  // 有拿不到图的风险，但NULL写入文件不会出错
    ret = saveJpg(photo_path, frame, frame_size);
    
    if (ret == 0) {
        m_album.album_picture_.updatePicture(photo_path);
    }

    xfree(frame);

    wsResNotifyCamFunctionState(CMD_NOTIFY_WIDE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_PHOTO, STATE_IDLE);
    wsCommonResponse(CMD_CAMERA_WIDE_PHOTOGRAPH, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    return ret;
}

int CamWide::photograph(WsPacket& ws_packet) {
    int ret = photograph();

    return 0;
}

int CamWide::burstPhoto() {
    int ret;
    uint8_t* frame = NULL;
    int frame_size = 0;

    unique_lock<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        wsCommonResponse(CMD_CAMERA_WIDE_BURST, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);
        return ret;
    }

    if (getPhotoState() != CAM_WIDE_PHOTO_IDLE) {
        log_e("cam wide is photographing");
        wsCommonResponse(CMD_CAMERA_WIDE_BURST, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, WS_OK);
        return 0;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");
        wsCommonResponse(CMD_CAMERA_WIDE_BURST, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, -WS_SDCARD_NOT_EXIST);
        return -WS_SDCARD_NOT_EXIST;
    }

    string path = getTimeStamp();
    burst_photo_path = album_path + burst_path + burst_photo_path_prefix + path;
    log_i("burst photo path is %s", burst_photo_path.c_str());
    string cmd = "mkdir " + burst_photo_path;
    system(cmd.c_str());

    setPhotoState(CAM_WIDE_PHOTO_BURST);
    burst_photo_count = getBurstPhotoCount();
    burst_photo_id = 0;

    lock.unlock();  //保证连拍过程中可以执行停止命令

    wsResNotifyCamFunctionState(CMD_NOTIFY_WIDE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_BURST, STATE_RUNNING);

    while (getPhotoState() == CAM_WIDE_PHOTO_BURST) {
        getCamWideJgpFrame(frame, frame_size);
        string jpg_path = burst_photo_path + "/" + to_string(burst_photo_id) + ".jpg";
        ret = saveJpg(jpg_path, frame, frame_size);
        xfree(frame);

        if (ret < 0) {
            break;
        }

        burst_photo_id++;
        log_i("burst_photo_id = %d, count= %d", burst_photo_id, burst_photo_count);
        wsResNotifyBurstProgress(CMD_NOTIFY_WIDE_BURST_PROGRESS, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, burst_photo_count, burst_photo_id);

        if (burst_photo_id == burst_photo_count) {
            break;
        }

        if (burst_photo_interval > 0)
            sleep(burst_photo_interval);
    }

    if (burst_photo_id > 0)
        m_album.album_burst_.updateBurst(burst_photo_path);

    if (getPhotoState() == CAM_WIDE_PHOTO_IDLE) {
        wsCommonResponse(CMD_CAMERA_WIDE_STOP_BURST, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, WS_OK);
    } else {
        wsCommonResponse(CMD_CAMERA_WIDE_BURST, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);
    }

    setPhotoState(CAM_WIDE_PHOTO_IDLE);
    wsResNotifyCamFunctionState(CMD_NOTIFY_WIDE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_BURST, STATE_IDLE);

    return ret;
}
    
int CamWide::burstPhoto(WsPacket& ws_packet) {
    int ret = burstPhoto();

    return 0;
}

int CamWide::stopBurstPhoto() {
    lock_guard<mutex> lock(camera_mutex);
    setPhotoState(CAM_WIDE_PHOTO_IDLE);

    return 0;
}

int CamWide::stopBurstPhoto(WsPacket& ws_packet) {
    int ret = stopBurstPhoto();

    return 0;
}

void CamWide::setPhotoState(CamWidePhotoState state) {
    log_i("set photo state = %d", state);
    photo_state = state;
}

CamWidePhotoState CamWide::getPhotoState() {
    return photo_state;
}

void CamWide::timeLapseGetNv12() {
    int ret =0;
    uint8_t* frame = NULL;
    int frame_size;
    int nv12_size;

    log_i("start timeLapseGet");

    while (getTimelapseState() == CAM_WIDE_TIMELAPSE_RECORDING) {
        usleep(450 * 1000);                                         
        getCamWideJgpFrame(frame, frame_size);
        cv::Mat nv12_mat = decodeJpegToNV12(frame, frame_size);     //单帧解码耗时150ms-160ms
        xfree(frame);

        nv12_size = nv12_mat.total() * nv12_mat.elemSize();
        // 将NV12数据复制到MEDIA_BUFFER
        uint8_t* buf_data = (uint8_t*)malloc(nv12_size);
        memcpy(buf_data, nv12_mat.data, nv12_size);

        pthread_mutex_lock(&timelapse_nv12_mutex);
        timelapse_nv12_size = nv12_size;
        timelapse_nv12_ptr = buf_data;
        pthread_cond_broadcast(&timelapse_nv12_cond);
        pthread_mutex_unlock(&timelapse_nv12_mutex);
        usleep(50 * 1000);                                          //保证getCamWideJgpFrame()可以获取到正确的内存图片数据
        xfree(buf_data);	
    }
}

int CamWide::startTimeLapsePhotograph(WsPacket& ws_packet) {
    int ret;
    ReqStartTimeLapse req_start_timelapse;

    unique_lock<mutex> lock(camera_mutex);
    string serialized_data = ws_packet.data();
    if (!req_start_timelapse.ParseFromString(serialized_data)) {
        log_i("protobuf parse error");
        wsCommonResponse(CMD_CAMERA_WIDE_START_TIMELAPSE_PHOTO, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, -WS_PARSE_PROTOBUF_ERROR);
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int fps = 30;
    timelapse_interval = getTimelapseInterval();
    int total_time = getTimelapseTotalTime();
    if (total_time == 0)
        total_time = INT_MAX;
    int out_time = total_time / (30.0 * timelapse_interval);
    
    if ((ret = isCameraOpen()) < 0) {
        state_manager->rgb_power_.setRgbEffect(RGB_EFFECT_FAIL);
        wsCommonResponse(CMD_CAMERA_WIDE_START_TIMELAPSE_PHOTO, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);
        return ret;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");
        state_manager->rgb_power_.setRgbEffect(RGB_EFFECT_FAIL);
        wsCommonResponse(CMD_CAMERA_WIDE_START_TIMELAPSE_PHOTO, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, -WS_SDCARD_NOT_EXIST);
        return -WS_SDCARD_NOT_EXIST;
    }

    if (getTimelapseState() == CAM_WIDE_TIMELAPSE_RECORDING) {
        log_e("cam tele is timelapse recording");
        wsCommonResponse(CMD_CAMERA_WIDE_START_TIMELAPSE_PHOTO, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, WS_OK);
        return 0;
    }
    
    setTimelapseState(CAM_WIDE_TIMELAPSE_RECORDING);
    log_i("start timelapse photo, timelapse_interval = %d, total_time = %d", timelapse_interval, total_time);

    wsResNotifyCamFunctionState(CMD_NOTIFY_WIDE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_TIMELAPSE, STATE_RUNNING);
    state_manager->rgb_power_.setRgbEffect(RGB_EFFECT_WORKING_STATE);

    timelapse_timer_manager.startTimer([&](int elapsed_time) {
        log_i("out time = %d", elapsed_time);
        timelapse_elapsed_time = elapsed_time;
        timelapse_out_time = elapsed_time / (30 * timelapse_interval);
        wsResNotifyTimelapseOutTime(CMD_NOTIFY_WIDE_TIMELAPSE_OUT_TIME, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, timelapse_interval, timelapse_out_time, elapsed_time);
    }, 1);

    string file_name = getTimeStamp();
    string video_path_h264 = album_path + video_path + timelapse_name_prefix + file_name + ".h264";
    string video_path_mp4 = album_path + video_path + timelapse_name_prefix + file_name + ".mp4";
    log_i("video path h264 = %s, mp4 = %s", video_path_h264.c_str(), video_path_mp4.c_str());

    thread timelapse_getMB_t(&CamWide::timeLapseGetNv12, this);
    timelapse_getMB_t.detach();

    camWideStartEncoder(video_path_h264.c_str(), timelapse_interval, out_time);

    if (getTimelapseState() == CAM_WIDE_TIMELAPSE_IDLE) {  // 主动停止
        wsCommonResponse(CMD_CAMERA_WIDE_STOP_TIMELAPSE_PHOTO, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, 0);
    } else {
        wsCommonResponse(CMD_CAMERA_WIDE_START_TIMELAPSE_PHOTO, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, 0);
    }

    setTimelapseState(CAM_WIDE_TIMELAPSE_IDLE);
    timelapse_timer_manager.stopTimer();

    wsResNotifyCamFunctionState(CMD_NOTIFY_WIDE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_TIMELAPSE, STATE_IDLE);
    state_manager->rgb_power_.setRgbEffect(RGB_EFFECT_SUCCESS);

    std::thread h264_to_mp4([video_path_h264, video_path_mp4]() {
        //转码成mp4
        string cmd_h264tomp4 = "ffmpeg -f h264 -i " + video_path_h264 + " -vcodec copy " + video_path_mp4;
        system(cmd_h264tomp4.c_str());

        //删除h264文件
        string cmd_delH264 = "rm " + video_path_h264;
        system(cmd_delH264.c_str());

        // string thumbnail_path = album_path + video_path + video_thumbnail_path + timelapse_name_prefix + file_name + ".jpg";
        // log_i("timelapse thumbnail path = %s", thumbnail_path.c_str());
        // string cmd_save_thumbnail = "ffmpeg -i " + video_path_mp4 + "-vframes 1 " + thumbnail_path;
        // system(cmd_save_thumbnail.c_str());

        record_loading_count += 1;
        system("sync");
        m_album.album_video_.updateVideo(video_path_mp4);
        record_loading_count -= 1;
        wsNotifyAlbumUpdate(CMD_NOTIFY_ALBUM_UPDATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, MEDIA_VIDEO);
    });
    h264_to_mp4.detach();

    return 0;
}

int CamWide::stopTimeLapsePhotograph() {
    log_i("stop timelapse photo");
    setTimelapseState(CAM_WIDE_TIMELAPSE_IDLE);
    wsResNotifyCamFunctionState(CMD_NOTIFY_WIDE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_TIMELAPSE, STATE_STOPPING);

    return 0;
}
    
int CamWide::stopTimeLapsePhotograph(WsPacket& ws_packet) {
    stopTimeLapsePhotograph();

    return 0;
}

void CamWide::setTimelapseState(CamWideTimelapseState state) {
    timelapse_state = state;
}
    
CamWideTimelapseState CamWide::getTimelapseState() {
    return timelapse_state;
}

int CamWide::getAllParams(WsPacket& ws_packet) {
    int ret;
    double exp_value;
    int exp_mode = 0;
    int exp_index = 75;
    int gain_mode = 1;
    int gain_value;
    int gain_index = 0;
    int ircut_value = 0;
    int wb_mode = 0;
    int wb_ct_value;
    int wb_ct_index = 51;
    int brightness = 0, contrast = 0, hue = 0, saturation = 60, sharpness = 17;
    int brightness_mapping_value = 0, contrast_mapping_value = 0, hue_mapping_value = 0, saturation_mapping_value = 60, sharpness_mapping_value = 17;

    unique_lock<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        wsResGetAllParams(CMD_CAMERA_WIDE_GET_ALL_PARAMS, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret, exp_mode, exp_index, gain_mode, gain_index,
                        ircut_value, wb_mode, wb_ct_index, 0, brightness, contrast, hue, saturation, sharpness, 30);

        return ret;
    }
    
    lock.unlock();
    getExpMode(&exp_mode);
    if (exp_mode == 3)
        exp_mode = 0;
    getExp(&exp_value);
    exp_index = findClosestExpIndex(exp_settings, exp_value);
    gain_index = findIntValueIndex(gain_settings, gain_value);
    if (gain_index == -1)
        gain_index = 0;
    getWBMode(&wb_mode);
    wb_mode = 1 - wb_mode;
    getWBCT(&wb_ct_value);
    wb_ct_index = findIntValueIndex(wb_settings, wb_ct_value);
    getBrightness(&brightness);
    brightness_mapping_value = brightness * 100.0 / 64;
    getContrast(&contrast);
    contrast_mapping_value = contrast * 200.0 / 95 - 100;
    getHue(&hue);
    hue_mapping_value = hue * 180.0 / 2000;
    getSaturation(&saturation);
    saturation_mapping_value = saturation * 200.0 / 100 - 100;
    getSharpness(&sharpness);
    sharpness_mapping_value = (sharpness - 1) * 100.0 / 6;

    wsResGetAllParams(CMD_CAMERA_WIDE_GET_ALL_PARAMS, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret, exp_mode, exp_index, gain_mode, gain_index,
                    ircut_value, wb_mode, wb_ct_index, 0, brightness_mapping_value, contrast_mapping_value, hue_mapping_value, saturation_mapping_value, sharpness, 30);
    

    log_i("exp_mode = %d", exp_mode);
    log_i("exp_index = %d", exp_index);
    log_i("gain_mode = %d", gain_mode);
    log_i("gain_index = %d", gain_index);
    log_i("ircut_value = %d", ircut_value);
    log_i("wb_mode = %d", wb_mode);
    log_i("wb_ct_index = %d", wb_ct_index);
    log_i("brightness = %d", brightness);
    log_i("contrast = %d", contrast);
    log_i("hue = %d", hue);
    log_i("saturation = %d", saturation);
    log_i("sharpness = %d", sharpness);

    return 0;
}

int CamWide::setAllParams(int exp_mode, int exp_index, int gain_index, int wb_mode,
                            int wb_index, int brightness, int contrast, int hue, int saturation, int sharpness) {
    
    int ret;

    log_i("exp_mode = %d", exp_mode);
    log_i("exp_index = %d", exp_index);
    log_i("gain_index = %d", gain_index);
    log_i("wb_mode = %d", wb_mode);
    log_i("wb_index = %d", wb_index);
    log_i("brightness = %d", brightness);
    log_i("contrast = %d", contrast);
    log_i("hue = %d", hue);
    log_i("saturation = %d", saturation);
    log_i("sharpness = %d", sharpness);

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        wsCommonResponse(CMD_CAMERA_WIDE_SET_ALL_PARAMS, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);
        return ret;
    }

    setExpMode(exp_mode);
    usleep(100 * 1000);
    double exp_value = getExpByIndex(exp_settings, exp_index);
    if (exp_value > 0) {
        ret = setExp(exp_value);
    } else {
        return -WS_INVAID_PARAM;
    }

    int gain_value = getIntValueByIndex(gain_settings, gain_index);
    if (gain_value > 0) {
        ret = setGain(gain_value);
    } else {
        ret = -WS_INVAID_PARAM;
    }

    setWBMode(wb_mode);

    if (wb_mode == 1)
        ret = setWBCT(wb_index);
    
    setBrightness(brightness);
    setContrast(contrast);
    setSaturation(saturation);
    setHue(hue);
    setSharpness(sharpness);

   return ret;                             
}

int CamWide::setAllParams(WsPacket& ws_packet) {
    int ret;
    ReqSetAllParams req_set_all_params;
    string serialized_data = ws_packet.data();

    if (!req_set_all_params.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int exp_mode = req_set_all_params.exp_mode();
    int exp_index = req_set_all_params.exp_index();
    int gain_index = req_set_all_params.gain_index();
    int wb_mode = req_set_all_params.wb_mode();
    int wb_index = req_set_all_params.wb_index();
    int brightness = req_set_all_params.brightness();
    int contrast = req_set_all_params.contrast();
    int hue = req_set_all_params.hue();
    int saturation = req_set_all_params.saturation();
    int sharpness = req_set_all_params.sharpness();

    ret = setAllParams(exp_mode, exp_index, gain_index, wb_mode, wb_index, brightness, contrast, hue, saturation, sharpness);

    wsCommonResponse(CMD_CAMERA_WIDE_SET_ALL_PARAMS, MODULE_CAMERA_WIDE, CMD_TYPE_RESPONSE, ret);

    return 0;
}

int CamWide::getRecordLoadingCount() {
    return record_loading_count;
}

int CamWide::getSessionBurstPhotoCount() {
    return burst_photo_count;
}

int CamWide::getBurstPhotoId() {
    return burst_photo_id;
}

int CamWide::getSessionTimelapseInterval() {
    return timelapse_interval;
}

int CamWide::getTimelapseElapsedTime() {
    return timelapse_elapsed_time;
}

int CamWide::getTimelapseOutTime() {
    return timelapse_out_time;
}